package edpool;

import java.util.concurrent.atomic.AtomicReference;

public class LockFreeExchanger<T>  
{
	AtomicReference<ExchangerState<T>> slot = new AtomicReference<ExchangerState<T>>(null);
	public ExchangeStatus exchange(ExchangerPackage<T> myItem, int maxRounds)
	{
		myItem.setEliminationState(EliminationStateEnum.None);
			
		ExchangerState<T> hisItem = slot.get();
		if (hisItem == null)
		{
				if (slot.compareAndSet(null, myItem.getWaitingStateObject())) 
				{
					while (maxRounds-- > 0)
					{   
						hisItem = slot.get();
						if (hisItem != myItem.getWaitingStateObject())
						{
							exchangeItems(myItem, hisItem);
							return ExchangeStatus.Success;							
						}
					}
								
					if (slot.compareAndSet(myItem.getWaitingStateObject(), null))
						return ExchangeStatus.Timeout;
					else 
					{
						exchangeItems(myItem, slot.get());
						return ExchangeStatus.Success;							
					}
				}
		}
		else
		{
			if(hisItem.getState() == ExchangerStateEnum.Waiting)
			{
				if(myItem.getPackageType() == hisItem.getPackageType())
				{
					if (slot.compareAndSet(hisItem, myItem.getDiffractingStateObject()))
					{
						myItem.setEliminationState(EliminationStateEnum.Diffracted);
						myItem.setDirection(1);
						return ExchangeStatus.Success;
					}
				}
				else
				{
					if (slot.compareAndSet(hisItem, myItem.getEliminatingStateObject()))
					{
						myItem.setEliminationState(EliminationStateEnum.Eliminated);
						if (myItem.getPackageType() == PackageTypeEnum.Consumer)
							myItem.setValue(hisItem.getValue());
						return ExchangeStatus.Success;
					}

				}			
			}
		}
		return ExchangeStatus.Failure;

	}
	private void exchangeItems(ExchangerPackage<T> myItem,	ExchangerState<T> yrItem)
	{
		if (yrItem.getState() == ExchangerStateEnum.Diffraction)
		{
			myItem.setDirection(0);
			myItem.setEliminationState(EliminationStateEnum.Diffracted);
		}
		else //Elimination
		{
			if (myItem.getPackageType() ==  PackageTypeEnum.Consumer)
				myItem.setValue(yrItem.getValue());

			myItem.setEliminationState(EliminationStateEnum.Eliminated);
		}		
		slot.set(null);

	}
	
}